﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using nl.tno.ict.multitouch.wpfmultitouch.events;
using System.Windows;
using System.Windows.Media;
using System.Windows.Input;
using System.ComponentModel;
using System.Windows.Controls.Primitives;

namespace nl.tno.ict.multitouch.wpfmultitouch.controls
{
    public class MTTextbox : TextBox, MTObject
    {
        private MTKeyboard _keyboard;
        private int _timeout = 1000;
        private int _caretIndex = 0;
        private FrameworkElement _tempParent;
        private int _fingerCount;


        #region MTObject Members

        public bool CanBeScaled { get { return MTProperties.GetCanBeScaled(this); } set { MTProperties.SetCanBeScaled(this, value); } }
        public bool CanBeDragged { get { return MTProperties.GetCanBeDragged(this); } set { MTProperties.SetCanBeDragged(this, value); } }
        public bool CanBeClicked { get { return MTProperties.GetCanBeClicked(this); } set { MTProperties.SetCanBeClicked(this, value); } }
        public bool CanBeRotated { get { return MTProperties.GetCanBeRotated(this); } set { MTProperties.SetCanBeRotated(this, value); } }
        public bool AdornersEnabled { get { return MTProperties.GetAdornersEnabled(this); } set { MTProperties.SetAdornersEnabled(this, value); } }
        public bool AutoGeneratedKeyboardColor { get { return MTProperties.GetAutoGeneratedKeyboardColor(this); } set { MTProperties.SetAutoGeneratedKeyboardColor(this, value); } }



        public MTCanvas KeyboardLayer
        {
            get { return (MTCanvas)GetValue(KeyboardLayerProperty); }
            set { SetValue(KeyboardLayerProperty, value); }
        }

        // Using a DependencyProperty as the backing store for KeyboardLayer.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty KeyboardLayerProperty =
            DependencyProperty.Register("KeyboardLayer", typeof(MTCanvas), typeof(MTTextbox), new UIPropertyMetadata(null));



        public bool IsDragging
        {
            get { return MTProperties.GetIsDragging(this); }
            set { MTProperties.SetIsDragging(this, value); }
        }

        public bool IsScaling
        {
            get { return MTProperties.GetIsScaling(this); }
            set { MTProperties.SetIsScaling(this, value); }
        }

        public bool IsRotating
        {
            get { return MTProperties.GetIsRotating(this); }
            set { MTProperties.SetIsRotating(this, value); }
        }

       
        #endregion

        public MTKeyboard Keyboard { get { return this._keyboard; } set { this._keyboard = value; } }

        /// <summary>
        /// Constructor of MTTextbox. 
        /// </summary>
        public MTTextbox()
        {
            // Sets the TouchEnabledObjectProperty for the element always true.
            MTProperties.SetTouchEnabled(this, true);
            this.GotFocus += new System.Windows.RoutedEventHandler(MTTextbox_GotFocus);
            this.LostFocus += new System.Windows.RoutedEventHandler(MTTextbox_LostFocus);
            this.Loaded += new RoutedEventHandler(MTTextbox_Loaded);
            this.FontSize = 18;
            this.TextWrapping = TextWrapping.Wrap;
            this.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
           
            this.MouseLeftButtonDown += new MouseButtonEventHandler(MTTextbox_MouseLeftButtonDown);
        }
        /// <summary>
        /// This event will take care for a full selection of the content of a textbox.
        /// </summary>
        /// <param name="sender">Sender of the event</param>
        /// <param name="e">MouseButtonEventArgs event</param>
        private void MTTextbox_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            this.SelectAll();
        }

        /// <summary>
        /// Will generate a random color to match the textboxborder with a keybaordborder.
        /// </summary>
        /// <param name="sender">Sender of the event</param>
        /// <param name="e">RoutedEventArgs event</param>
        private void MTTextbox_Loaded(object sender, RoutedEventArgs e)
        {
            // Sets with an autogenerated color the same brush for the textbox and the attached keyboard, if true.
            if (AutoGeneratedKeyboardColor)
            {
                Random ran = new Random();
                byte r = (byte)ran.Next(0, 255);
                byte g = (byte)ran.Next(0, 255);
                byte b = (byte)ran.Next(0, 255);
                Brush brush = new SolidColorBrush(Color.FromRgb(r, g, b));
                this.BorderThickness = new Thickness(5, 5, 5, 5);
                this.BorderBrush = brush;
                this._keyboard.KeyboardBorder.Background = brush;
                this._keyboard.KeyboardBorder.Background.Opacity = 0.8;
                this._keyboard.BorderThickness = new Thickness(1, 1, 1, 1);
                this._keyboard.BorderBrush = brush;
            }
        }

        /// <summary>
        /// Catches every key event and attaches it to the text of a textbox.
        /// </summary>
        /// <param name="sender">Sender of the event</param>
        /// <param name="ckea">ControlKeyboardEventArgs event</param>
        private void keyboard_keyboardHandler(object sender, ControlKeyboardEventArgs ckea)
        {
            if (this.SelectedText != "" && (!(ckea.Key == Key.Escape || ckea.Key == Key.CapsLock || ckea.Key == Key.LeftAlt || 
                ckea.Key == Key.LeftCtrl || ckea.Key == Key.LeftShift || ckea.Key == Key.RightShift || ckea.Key == Key.Up || 
                ckea.Key == Key.Down || ckea.Key == Key.Right || ckea.Key == Key.Left)))
            {
                this.Clear();
                this._caretIndex = 0;
            }
            
            switch (ckea.Key)
            {
                case Key.Back:
                    if (this.Text.Length > 0 && this._caretIndex > 0)
                    {
                        this.Text = this.Text.Remove(this._caretIndex - 1, 1);
                        this._caretIndex -= 1;
                    }
                    break;
                case Key.Escape:
                    _caretIndex = this.Text.Length;
                    if (_tempParent is MTCanvas)
                        ((MTCanvas)_tempParent).Children.Remove(_keyboard);
                    else if (_tempParent is MTGrid)
                        ((MTGrid)_tempParent).Children.Remove(_keyboard);
                    return;
                case Key.CapsLock:
                    this.Focus();
                    return;
                case Key.Enter:
                    this.Text = this.Text.Insert(this._caretIndex, "\r\n");
                    this._caretIndex += 2;
                    break;
                case Key.Space:
                    this.Text = this.Text.Insert(this._caretIndex, " ");
                    this._caretIndex += 1;
                    break;
                case Key.OemPeriod:
                    this.Text = this.Text.Insert(this._caretIndex, ".");
                    this._caretIndex += 1;
                    break;
                case Key.D1:
                    this.Text = this.Text.Insert(this._caretIndex, "1");
                    this._caretIndex += 1;
                    break;
                case Key.D2:
                    this.Text = this.Text.Insert(this._caretIndex, "2");
                    this._caretIndex += 1;
                    break;
                case Key.D3:
                    this.Text = this.Text.Insert(this._caretIndex, "3");
                    this._caretIndex += 1;
                    break;
                case Key.D4:
                    this.Text = this.Text.Insert(this._caretIndex, "4");
                    this._caretIndex += 1;
                    break;
                case Key.D5:
                    this.Text = this.Text.Insert(this._caretIndex, "5");
                    this._caretIndex += 1;
                    break;
                case Key.D6:
                    this.Text = this.Text.Insert(this._caretIndex, "6");
                    this._caretIndex += 1;
                    break;
                case Key.D7:
                    this.Text = this.Text.Insert(this._caretIndex, "7");
                    this._caretIndex += 1;
                    break;
                case Key.D8:
                    this.Text = this.Text.Insert(this._caretIndex, "8");
                    this._caretIndex += 1;
                    break;
                case Key.D9:
                    this.Text = this.Text.Insert(this._caretIndex, "9");
                    this._caretIndex += 1;
                    break;
                case Key.D0:
                    this.Text = this.Text.Insert(this._caretIndex, "0");
                    this._caretIndex += 1;
                    break;
                case Key.OemTilde:
                    this.Text = this.Text.Insert(this._caretIndex, "~");
                    this._caretIndex += 1;
                    break;
                case Key.OemMinus:
                    this.Text = this.Text.Insert(this._caretIndex, "-");
                    this._caretIndex += 1;
                    break;
                case Key.Tab:
                    this.Text = this.Text.Insert(this._caretIndex, "   ");
                    this._caretIndex += 3;
                    break;
                case Key.OemOpenBrackets:
                    this.Text = this.Text.Insert(this._caretIndex, "[");
                    this._caretIndex += 1;
                    break;
                case Key.OemCloseBrackets:
                    this.Text = this.Text.Insert(this._caretIndex, "]");
                    this._caretIndex += 1;
                    break;
                case Key.OemPipe:
                    this.Text = this.Text.Insert(this._caretIndex, "!");
                    this._caretIndex += 1;
                    break;
                case Key.Oem8:
                    this.Text = this.Text.Insert(this._caretIndex, ";");
                    this._caretIndex += 1;
                    break;
                case Key.OemQuotes:
                    this.Text = this.Text.Insert(this._caretIndex, "'");
                    this._caretIndex += 1;
                    break;
                case Key.OemComma:
                    this.Text = this.Text.Insert(this._caretIndex, ",");
                    this._caretIndex += 1;
                    break;
                case Key.OemQuestion:
                    this.Text = this.Text.Insert(this._caretIndex, "?");
                    this._caretIndex += 1;
                    break;
                case Key.LeftAlt:
                    this.Focus();
                    return;
                case Key.LeftCtrl:
                    this.Focus();
                    return;
                case Key.LeftShift:
                    this.Focus();
                    return;
                case Key.RightShift:
                    this.Focus();
                    return;
                case Key.Up:
                    this.LineUp();
                    break;
                case Key.Down:
                    this.LineDown();
                    break;
                case Key.Left:
                    if(this._caretIndex > 0)
                        this._caretIndex -= 1;
                    break;
                case Key.Right:
                    if(this._caretIndex < this.Text.Length)
                        this._caretIndex += 1;
                    break;
                case Key.A:
                    if(_keyboard.SelectText)
                    {                        
                        MouseButtonEventArgs mea = new MouseButtonEventArgs(Mouse.PrimaryDevice, 1, MouseButton.Left);
                        mea.RoutedEvent = MTTextbox.MouseLeftButtonDownEvent;
                        this.RaiseEvent(mea);

                        this.Focus();
                        this._keyboard.SelectText = false;
                        return;
                    }
                    else
                    {
                        if (_keyboard.IsUpperKey)
                            this.Text = this.Text.Insert(this._caretIndex, ckea.Key.ToString().ToUpper());
                        else
                            this.Text = this.Text.Insert(this._caretIndex, ckea.Key.ToString().ToLower());
                        this._caretIndex += 1;
                        break;
                    }
                default:
                    if (_keyboard.IsUpperKey)
                        this.Text = this.Text.Insert(this._caretIndex, ckea.Key.ToString().ToUpper());
                    else
                        this.Text = this.Text.Insert(this._caretIndex, ckea.Key.ToString().ToLower());
                    this._caretIndex += 1;
                    break;
            }            
            this.CaretIndex = this._caretIndex;
            this.Focus();
        }         

        /// <summary>
        /// Occers when the textbox will lose the focus.
        /// </summary>
        /// <param name="sender">Sender of the event</param>
        /// <param name="e">RoutedEventArgs event</param>
        private void MTTextbox_LostFocus(object sender, System.Windows.RoutedEventArgs e)
        {            
          
        }

        /// <summary>
        /// Occers when a textbox will get the focus.
        /// </summary>
        /// <param name="sender">Sender of the event</param>
        /// <param name="e">RoutedEventArgs event</param>
        private void MTTextbox_GotFocus(object sender, System.Windows.RoutedEventArgs e)
        {
        }

        /// <summary>
        /// Get the parent. This is always a canvas
        /// </summary>
        /// <param name="element">The element</param>
        /// <returns>The parent of the element</returns>
        private DependencyObject getParent(DependencyObject element)
        {
            if (((FrameworkElement)element).Parent != null)
            {
                if (!(((FrameworkElement)element).Parent is Window))
                {
                    if (((FrameworkElement)element).Parent is MTCanvas || ((FrameworkElement)element).Parent is MTGrid)
                        return ((FrameworkElement)element).Parent;
                    else
                        return getParent(((FrameworkElement)element).Parent);
                }
                else
                    return element;
            }
            return null;
        }

        #region MTObject Members

        /// <summary>
        /// Occers when a finger has pressed the textbox for an amound of time without moving it.
        /// The attached keyboard will show up now.
        /// </summary>
        void MTObject.NotMoved()
        {
            FrameworkElement frameworkElement = (FrameworkElement)this.Parent;

            _keyboard = new MTKeyboard();
            this.Keyboard = _keyboard;
            _keyboard.MTKeyboardButtonPressed += new ControlKeyboardButtonHandler(keyboard_keyboardHandler);

            this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, (Action)delegate()
            {
                if (KeyboardLayer != null)
                {
                    KeyboardLayer.Children.Add(_keyboard);
                    this.positioningKeyboard();
                }

                //if (_tempParent != null && _tempParent is MTCanvas)
                //{
                //    if (!((MTCanvas)_tempParent).Children.Contains(_keyboard))
                //    {
                //        ((MTCanvas)_tempParent).Children.Add(_keyboard);
                //        this.positioningKeyboard();
                //    }
                //}
                //else if (_tempParent != null && _tempParent is MTGrid)
                //{
                //    if (!((MTGrid)_tempParent).Children.Contains(_keyboard))
                //        ((MTGrid)_tempParent).Children.Add(_keyboard);
                //}
                //else
                //{
                //    while ((!(frameworkElement is MTCanvas)))
                //    {
                //        if (frameworkElement.Parent != null && (!(frameworkElement.Parent is Window)))
                //            frameworkElement = (FrameworkElement)this.getParent(frameworkElement);
                //        else
                //        {
                //            if (!(this.Parent is MTGrid))
                //                frameworkElement = (FrameworkElement)this.getParent(this);
                //            if ((!(frameworkElement is MTBorder)) && frameworkElement is MTGrid)
                //            {
                //                _tempParent = frameworkElement;
                //                ((MTGrid)frameworkElement).Children.Add(_keyboard);
                //                MTGrid.SetZIndex(_keyboard, 25);
                //                return;
                //            }
                //            return;
                //        }
                //    }
                //    _tempParent = frameworkElement;
                //    ((MTCanvas)frameworkElement).Children.Add(_keyboard);
                //    MTCanvas.SetZIndex(_keyboard, 25);
                //    this.positioningKeyboard();
                //    return;
                //}
                //this.CaretIndex = _caretIndex;
            });                       
        }

        /// <summary>
        /// The keyboard attached to the textbox will be positioned.
        /// </summary>
        private void positioningKeyboard()
        {
            
            MTCanvas.SetBottom(_keyboard, 100);
            MTCanvas.SetLeft(_keyboard, 300);
            
        }

        /// <summary>
        /// Gets or sets the timeout, in ms, used to by the NotMoved event. Default: 1000 ms
        /// </summary>
        int MTObject.Timeout
        {
            get{ return _timeout;}
            set{ _timeout = value;}
        }

        public int FingerCount
        {
            get
            {
                return _fingerCount;
            }
            set
            {
                _fingerCount = value;
            }
        }

        /// <summary>
        /// Gets or sets the onTop dependency property. When true, when a finger touches the object the object
        /// gets on top of the screen (z-index changes).
        /// </summary>
        public bool OnTop
        {
            get { return MTProperties.GetOnTop(this); }
            set { MTProperties.SetOnTop(this, value); }
        }

        /// <summary>
        /// Set the object TouchEnabled. When an object isn't touchEnabled interaction with fingers is not
        /// possible (nothing will happen).
        /// </summary>
        public bool TouchEnabled
        {
            get { return MTProperties.GetTouchEnabled(this); }
            set { MTProperties.SetTouchEnabled(this, value); }
        }

        #endregion


    }
}
